#pragma once

#include <iostream>
#include <cerrno>
#include <sys/types.h>
#include <sys/socket.h>
#include "include/Protocol.hpp"
#include "include/CalculateService.hpp"
#include "include/Log.hpp"
#include "Common.hpp"
#include "ConnectionFactory.hpp"

class EventHandler
{
public:
    EventHandler()
    {}

    static void acceptForSelf(Connection* conn)
    {
        errno = 0;
        while(true)
        {
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);
            int connect_fd = ::accept(conn->getSockFd(), (struct sockaddr*)&peer, &len);
            if(connect_fd > 0)
            {
                char buffer[64];
                inet_ntop(AF_INET, &peer.sin_addr, buffer, sizeof(buffer));
                conn->setClientIp(buffer);
                conn->setClientPort(ntohs(peer.sin_port));
                log.message(INFO, "get a new link, fd: %d", connect_fd);

                setNonBlock(connect_fd);
                auto reader = std::bind(&EventHandler::read, std::placeholders::_1);
                auto writer = std::bind(&EventHandler::write, std::placeholders::_1);
                auto excepter = std::bind(&EventHandler::except, std::placeholders::_1);
                conn->_reactorFrom->addConnection(ConnectionFactory::createNormalConnection(
                    connect_fd, reader, writer, excepter, EPOLLIN|EPOLLET, conn->_reactorFrom));
            }
            else
            {
                if(errno == EAGAIN) break; //数据取尽，无数据就绪
                else if(errno == EINTR) continue; //上次读取被信号中断
                else //发生错误
                {
                    log.message(WARN, "acceptForSelf error... %d: %s", errno, strerror(errno));
                    break;
                }
            }
        }
    }

    static void acceptForSubReactor(Connection* conn)
    {
        errno = 0;
        while(true)
        {
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);
            int connect_fd = ::accept(conn->getSockFd(), (struct sockaddr*)&peer, &len);
            if(connect_fd > 0)
            {
                setNonBlock(connect_fd);
                Connection* threadConn = conn->_reactorFrom->getSubReactorConnection();
                threadConn->appendOutBuffer(std::to_string(0) + std::to_string(connect_fd));
                log.message(INFO, "have a look, write a fd string: %d", connect_fd);
                write(threadConn);
                fflush(nullptr);
            }
            else
            {
                if(errno == EAGAIN) break; //数据取尽，无数据就绪
                else if(errno == EINTR) continue; //上次读取被信号中断
                else //发生错误
                {
                    log.message(WARN, "acceptForSubReactor error... %d: %s", errno, strerror(errno));
                    break;
                }
            }
        }
    }

    static void handlerRequest(Connection* conn)
    {
        std::string& inBuffer = conn->getInBuffer();
        log.message(INFO, "have a look: %s", inBuffer.c_str());
        if(inBuffer[0] == '0')
        {
            inBuffer.erase(0, 1);
            int connect_fd = std::stoi(inBuffer);
            setNonBlock(connect_fd);
            auto reader = std::bind(&EventHandler::read, std::placeholders::_1);
            auto writer = std::bind(&EventHandler::write, std::placeholders::_1);
            auto excepter = std::bind(&EventHandler::except, std::placeholders::_1);
            conn->_reactorFrom->addConnection(ConnectionFactory::createNormalConnection(
                connect_fd, reader, writer, excepter, EPOLLIN|EPOLLET, conn->_reactorFrom));
            inBuffer.clear();
        }
        else 
        {
            std::string content;
            DLP::ReqRespFactory factory;
            CalculateService calculateService;
            while(DLP::dlpDecoding(inBuffer, content))
            {
                std::shared_ptr<DLP::Request> req = factory.makeRequest();
                if(req->deserialize(content)) 
                {
                    int code = -1;
                    int result = calculateService.doCalculate(req->getX(), req->getY(), req->getOp(), code);
                    std::shared_ptr<DLP::Response> resp = factory.makeResponse(result, code);
                    std::string dlpResponse = DLP::dlpEncoding(resp->serialize());
                    conn->appendOutBuffer(dlpResponse);
                }
            }
            if(!conn->isOutBufferEmpty())
            {
                write(conn);
            }
        }
    }

    static void read(Connection* conn)
    {
        errno = 0;
        char buffer[1024];
        while(true)
        {
            // 只负责把本轮数据读尽，不关心数据的格式/协议
            ssize_t n = ::read(conn->getSockFd(), buffer, sizeof(buffer)-1);
            if(n > 0)
            {
                buffer[n] = '\0';
                conn->appendInBuffer(buffer);
            }
            else
            {
                if(errno == EAGAIN) break; //数据取尽，无数据就绪
                else if(errno == EINTR) continue; //上次读取被信号中断
                else //发生错误 / 客户端正常退出，连接关闭，属于读事件就绪，::read返回0
                {
                    except(conn);
                    log.message(WARN, "EventHandler::read error... %d: %s", errno, strerror(errno));
                    return;
                }
            }
        }
        std::cout << "fd# " << conn->getSockFd() << std::endl << "接收缓冲区：" << conn->getInBuffer() << std::endl;
        // EAGAIN
        handlerRequest(conn);
    }

    static void write(Connection* conn)
    {
        errno = 0;
        std::string& outBuffer = conn->getOutBuffer();
        while(true)
        {
            ssize_t n = ::write(conn->getSockFd(), outBuffer.c_str(), outBuffer.size());
            if(n > 0)
            {
                outBuffer.erase(0, n); //已经发送的，从缓冲区移除
                if(outBuffer.empty()) break;
            }
            else
            {
                if(errno == EAGAIN) break;
                else if(errno == EINTR) continue;
                else
                {
                    except(conn);
                    log.message(WARN, "EventHandler::write error... %d: %s", errno, strerror(errno));
                    return;
                }
            }
        }

        // 写事件按需关心
        if(conn->isOutBufferEmpty())
        {
            // outBuffer.empty()跳出，数据发完，关闭对写事件的关心
            conn->_reactorFrom->enableEvent(conn->getSockFd(), true, false);
        }
        else
        {
            // EAGAIN跳出，内核发送缓冲区写满，开启对写事件EPOLLOUT的关心
            // 写事件一旦就绪（内核缓冲区腾出空间），会通过事件派发器再次回调到此方法，发送剩余数据
            conn->_reactorFrom->enableEvent(conn->getSockFd(), true, true);
        }
    }

    static void except(Connection* conn)
    {
        log.message(INFO, "EventHandler::except, connection erase, fd: %d", conn->getSockFd());
        // epoll中移除红黑树节点
        // unordered_map中移除conn项
        conn->_reactorFrom->removeConnection(conn);
        // 关fd
        conn->closeFd();
        // 删conn
        delete conn;
    }

    ~EventHandler()
    {}
};